#ifndef _OYUN_IM_H
#define _OYUN_IM_H

// #include "config.h"
#include "hps.h"
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <map>
#include <regex>
#include <set>
#include <sstream>
#include <sys/mman.h>
#include <unistd.h>
#include <unordered_map>
#include <utility>
#include <vector>
#include <algorithm>
#define QUICK_PHRASE_DIR "/home/kylin/fcitx/src/im/oyun/data/pySym.txt"
#define OYUN_DICT "/home/kylin/fcitx/src/im/oyun/data/dict.oyun"
#define USER_DEFINED_WORD_LIST "/home/kylin/fcitx/src/im/oyun/data/user_dict.txt"
#define WORD_LIST "/home/kylin/fcitx/src/im/oyun/data/data.txt"
#define GRAM3 "/home/kylin/fcitx/src/im/oyun/data/3-gram.txt"
#define GRAM2 "/home/kylin/fcitx/src/im/oyun/data/2-gram.txt"
#define CONFIG "/home/kylin/fcitx/src/im/oyun/data/oyun.config"
#define MAX_QUICK_PHRASE 100
using std::map;
using std::set;
using std::string;
using std::unordered_map;
using std::vector;
using namespace std;

string do_mn2latin(const string &query, bool code_switch);
string do_latin2mn(string query, bool code_switch);
string key_transform(string s, bool state);

struct Phrase {
    char abbr[20];
    char phrase[100];
    int position;

    Phrase(const string &phrase, int position) {
        strcpy(this->phrase, phrase.c_str());
        this->position = position;
    }

    Phrase(const string &abbr, const string &phrase, int position) {
        strcpy(this->abbr, abbr.c_str());
        strcpy(this->phrase, phrase.c_str());
        this->position = position;
    }
};

class IM_Config {
private:
    // 用户输入相关
    int today_input;
    int total_input;
    int kpm;
    int rank;
    // 模糊搜索开关
    bool search_switch_vq;
    bool search_switch_ou;
    bool search_switch_td;
    bool search_switch_hg;
    bool search_switch_eE;
    bool search_switch_wE;
    bool search_switch_ngN;
    bool search_switch_sixi;
    // 编码标准
    bool code_switch; // true:共享工程编码   false:国标编码
    // 键盘布局
    bool key_switch; // true:国标布局   false:蒙科立布局
    // 字体选择
    int font; // 1--8 选择不同字体
public:
    char symbols[20];
    char asr_content[200];
    int quick_phrase_index{0};
    Phrase quick_phrase_list[MAX_QUICK_PHRASE];
    // get set方法
    bool get_search_switch_vq() const { return search_switch_vq; }

    void set_search_switch_vq(bool value) {
        this->search_switch_vq = value;
        set_config("search_switch_vq", search_switch_vq ? "1" : "0");
    }

    bool get_search_switch_ou() const { return search_switch_ou; }

    void set_search_switch_ou(bool value) {
        this->search_switch_ou = value;
        set_config("search_switch_ou", search_switch_ou ? "1" : "0");
    }

    bool get_search_switch_td() const { return search_switch_td; }

    void set_search_switch_td(bool value) {
        this->search_switch_td = value;
        set_config("search_switch_td", search_switch_td ? "1" : "0");
    }

    bool get_search_switch_hg() const { return search_switch_hg; }

    void set_search_switch_hg(bool value) {
        this->search_switch_hg = value;
        set_config("search_switch_hg", search_switch_hg ? "1" : "0");
    }

    bool get_search_switch_eE() const { return search_switch_eE; }

    void set_search_switch_eE(bool value) {
        this->search_switch_eE = value;
        set_config("search_switch_eE", search_switch_eE ? "1" : "0");
    }

    bool get_search_switch_wE() const { return search_switch_wE; }

    void set_search_switch_wE(bool value) {
        this->search_switch_wE = value;
        set_config("search_switch_wE", search_switch_wE ? "1" : "0");
    }

    bool get_search_switch_ngN() const { return search_switch_ngN; }

    void set_search_switch_ngN(bool value) {
        this->search_switch_ngN = value;
        set_config("search_switch_ngN", search_switch_ngN ? "1" : "0");
    }

    bool get_search_switch_sixi() const { return search_switch_sixi; }

    void set_search_switch_sixi(bool value) {
        this->search_switch_sixi = value;
        set_config("search_switch_sixi", search_switch_sixi ? "1" : "0");
    }

    bool get_code_switch() const { return code_switch; }

    void set_code_switch(bool value) {
        this->code_switch = value;
        set_config("code_switch", code_switch ? "1" : "0");
    }

    bool get_key_switch() const { return key_switch; }

    void set_key_switch(bool value) {
        this->key_switch = value;
        set_config("key_switch", key_switch ? "1" : "0");
    }

    int get_font() const { return font; }

    void set_font(int value) {
        this->font = value;
        set_config("font", to_string(font));
    }

    // 获取今天的输入
    int get_today_input() const { return today_input; }

    // 设置今天的输入
    void set_today_input(int input) { this->today_input = input; }

    // 获取总输入
    int get_total_input() const { return total_input; }

    // 设置总输入
    void set_total_input(int input) {
        this->total_input = input;
        //        //set_config("search_switch_sixi", search_switch_sixi ? "1" :
        //        "0");
    }

    // 获取每分钟按键数
    int get_kpm() const { return kpm; }

    // 设置每分钟按键数
    void set_kpm(int input) { this->kpm = input; }

    // 获取排名
    int get_rank() const {
        return rank;
    }

    // 设置排名
    void set_rank(int r) { this->rank = r; }
    void set_config(const string &item, const string &state);
};

struct Word {
    bool user_defined;
    unsigned consonant_length;
    unsigned frequency;
    bool end_with_consonant;
    string word;
    string latin;
    unsigned widx;
    Word() {}

    Word(string word, string latin) : word(word), latin(latin) {}

    Word(bool user_defined, unsigned consonant_length, unsigned frequency,
         bool end_with_consonant, string word, string latin, unsigned widx)
        : user_defined(user_defined), consonant_length(consonant_length),
          frequency(frequency), end_with_consonant(end_with_consonant),
          word(word), latin(latin), widx(widx) {}

    template <class B>
    void serialize(B &buf) const {
        buf << user_defined << consonant_length << frequency
            << end_with_consonant << word << latin << widx;
    }

    template <class B>
    void parse(B &buf) {
        buf >> user_defined >> consonant_length >> frequency >>
            end_with_consonant >> word >> latin >> widx;
    }
};

struct WordTree {
    std::map<char, WordTree> tree;
    vector<unsigned> words;

    template <class B>
    void serialize(B &buf) const {
        buf << tree << words;
    }

    template <class B>
    void parse(B &buf) {
        buf >> tree >> words;
    }
};

struct GramItem {
    unsigned word;
    unsigned frequency;

    GramItem(unsigned word, unsigned frequency)
        : word(word), frequency(frequency) {}

    template <class B>
    void serialize(B &buf) const {
        buf << word << frequency;
    }

    template <class B>
    void parse(B &buf) {
        buf >> word >> frequency;
    }
};

struct Gram2 {
    std::map<unsigned, vector<GramItem>> gram;

    void sort() {
        for (auto &pair : gram) {
            std::vector<GramItem> &v = pair.second;
            std::sort(v.begin(), v.end(),
                      [](const GramItem &a, const GramItem &b) {
                          return a.frequency > b.frequency;
                      });
        }
    }

    template <class B>
    void serialize(B &buf) const {
        buf << gram;
    }

    template <class B>
    void parse(B &buf) {
        buf >> gram;
    }
};

struct Gram3 {
    std::map<unsigned, Gram2> gram;

    void sort() {
        std::for_each(
            gram.begin(), gram.end(),
            [](std::pair<unsigned, Gram2> element) { element.second.sort(); });
    }

    template <class B>
    void serialize(B &buf) const {
        buf << gram;
    }

    template <class B>
    void parse(B &buf) {
        buf >> gram;
    }
};

struct IM_Data {
    vector<Word> word_list;
    WordTree tree;
    Gram2 gram2;
    Gram3 gram3;

    template <class B>
    void serialize(B &buf) const {
        buf << tree << word_list << gram2 << gram3;
    }

    template <class B>
    void parse(B &buf) {
        buf >> tree >> word_list; // >> gram2 >> gram3;
    }
};

class OyunIM {
public:
    vector<Word> do_query(const string &query, int flag);

    vector<Word> query_next(const string &query);

    vector<Word> use_digit_hotkey_search_lattice_suffix(const int index) const;

    vector<Word> search_lattice_suffix(const string &s) const;

    string get_current_word_latin(const string &query);

    OyunIM(IM_Config *config, const string &dictionary = OYUN_DICT,
           const string &user_defined_word_list = USER_DEFINED_WORD_LIST,
           const string &build_in_word_list = WORD_LIST,
           const string &gram2 = GRAM2, const string &gram3 = GRAM3,
           const string &quick_phrase_dir = QUICK_PHRASE_DIR,
           const string &config_dir = CONFIG);

    string get_quick_phrase_by_page(int page);

    bool insert_quick_phrase(const string &abbr, const string &phrase,
                             int position);

    bool insert_quick_phrase_in_file(const string &abbr, const string &phrase,
                                     int position);

    bool modify_quick_phrase(const string &new_abbr, const string &old_phrase,
                             const string &new_phrase, int new_position);

    bool modify_quick_phrase_in_file(const string &new_abbr,
                                     const string &old_phrase,
                                     const string &new_phrase,
                                     int new_position);

    bool remove_quick_phrase(const string &phrase);

    bool remove_quick_phrase_in_file(const string &phrase);

private:
    void update_frequency(unsigned widx);

    string asr_postprocess(string res);

    vector<string> lattice_suffix_recommendation(const string &s);

    vector<unsigned> lookup(const string &query);

    vector<string> extend_query(string s);

    set<unsigned> search(const vector<string> &query, int consonant_length,
                            bool skip_vowel);

    vector<Word> gram_recommendation();

    void set_stub(const string &query);

    void save_dictionary();

    void make_tree(const string &file_name, bool user_defined);

    void make_gram(const string &gram2, const string &gram3);

    vector<Phrase> search_quick_phrase(const string &abbr) const;

private:
    IM_Data d;
    Word word_stub;
    unsigned history1, history2;

public:
    IM_Config *imConfig;
};

#endif
